// Examples/decoder.cpp

/* 
This program takes as input an inputfile, k, m, a coding
technique, w, and packetsize.  It is the companion program
of encoder.c, which creates k+m files.  This program assumes 
that up to m erasures have occurred in the k+m files.  It
reads in the k+m files or marks the file as erased. It then
recreates the original file and creates a new file with the
suffix "decoded" with the decoded contents of the file.

This program does not error check command line arguments because 
it is assumed that encoder.c has been called previously with the
same arguments, and encoder.c does error check.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <signal.h>
#include <vector>
#include "jerasure-2.h"
#include "reed_sol-2.h"
#include "galois-2.h"
#include "cauchy-2.h"
#include "liberation-2.h"

#define N 10

enum Coding_Technique {Reed_Sol_Van, Reed_Sol_R6_Op, Cauchy_Orig, Cauchy_Good, Liberation, Blaum_Roth, Liber8tion, RDP, EVENODD, No_Coding};

char *Methods[N] = {(char *)"reed_sol_van", (char *)"reed_sol_r6_op", (char *)"cauchy_orig", (char *)"cauchy_good", (char *)"liberation", (char *)"blaum_roth", (char *)"liber8tion", (char *)"rdp", (char *)"evenodd", (char *)"no_coding"};

/* Global variables for signal handler */
enum Coding_Technique method;
int readins, n;

/* Function prototype */
void ctrl_bs_handler(int dummy);

int main (int argc, char **argv) {
	FILE *fp;				// File pointer

	/* Jerasure arguments */
	JER_Slices * slices;
	JER_Matrix *matrix;
	JER_Bitmatrix *bitmatrix;
	vector <int> erasures;
	vector <int> erased;
	
	/* Parameters */
	int k, m, w, packetsize, buffersize;
	enum Coding_Technique tech;
	char *c_tech;
	
	int i, j;				// loop control variables
	int blocksize;			// size of individual files
	int origsize;			// size of file before padding
	int total;				// used to write data, not padding to file
	struct stat status;		// used to find size of individual files
	int numerased;			// number of erased files
		
	/* Used to recreate file names */
	char *temp;
	char *cs1, *cs2;
	char *fname;
	int md;
	char *curdir;

	/* Used to time decoding */
	struct timeval t1, t2, t3, t4;
	struct timezone tz;
	double tsec;
	double totalsec;

	signal(SIGQUIT, ctrl_bs_handler);

	matrix = NULL;
	bitmatrix = NULL;
	totalsec = 0.0;
	
	/* Start timing */
	gettimeofday(&t1, &tz);

	/* Error checking parameters */
	if (argc != 2) {
		fprintf(stderr, "usage: inputfile\n");
		exit(0);
	}
	curdir = (char *)malloc(sizeof(char)*100);
	getcwd(curdir, 100);
	
	/* Begin recreation of file names */
	cs1 = (char*)malloc(sizeof(char)*strlen(argv[1]));
	cs2 = strrchr(argv[1], '/');
	if (cs2 != NULL) {
		cs2++;
		strcpy(cs1, cs2);
	}
	else {
		strcpy(cs1, argv[1]);
	}
	cs2 = strchr(cs1, '.');
	if (cs2 != NULL) {
		*cs2 = '\0';
	}	
	cs2 = (char*)malloc(sizeof(char)*strlen(argv[1]));
	fname = strchr(argv[1], '.');
	if(fname != NULL){
		strcpy(cs2, fname);
	}
	fname = (char *)malloc(sizeof(char*)*(100+strlen(argv[1])+10));

	/* Read in parameters from metadata file */
	sprintf(fname, "%s/Coding/%s_meta.txt", curdir, cs1);

	fp = fopen(fname, "rb");
	temp = (char *)malloc(sizeof(char)*(strlen(argv[1])+10));
	fscanf(fp, "%s", temp);	
	
	if (fscanf(fp, "%d", &origsize) != 1) {
		fprintf(stderr, "Original size is not valid\n");
		exit(0);
	}
	if (fscanf(fp, "%d %d %d %d %d", &k, &m, &w, &packetsize, &buffersize) != 5) {
		fprintf(stderr, "Parameters are not correct\n");
		exit(0);
	}
	c_tech = (char *)malloc(sizeof(char)*(strlen(argv[1])+10));
	fscanf(fp, "%s", c_tech);
	fscanf(fp, "%d", (int *)(&tech));
	method = tech;
	fscanf(fp, "%d", &readins);
	fclose(fp);	

	/* Allocate memory */
	erased.resize(k+m,0);

	slices = new JER_Slices;
	slices->ptrs.resize(k+m);
	slices->K = k;
	slices->M = m;
	slices->W = w;
	slices->PS = packetsize; 
	//allocate space for the slices of coding data
	if (buffersize != origsize) {
		blocksize = buffersize/k;
		for (i = 0; i < k+m; i++) {
			slices->ptrs[i] = (unsigned char *)malloc(sizeof(unsigned char)*blocksize);
		}
	}

	sprintf(temp, "%d", k);
	md = strlen(temp);
	gettimeofday(&t3, &tz);

	/* Create coding matrix or bitmatrix */
	switch(tech) {
		case No_Coding:
			break;
		case Reed_Sol_Van:
			matrix = reed_sol_vandermonde_coding_matrix(k, m, w);
			break;
		case Reed_Sol_R6_Op:
			matrix = reed_sol_r6_coding_matrix(k, w);
			break;
		case Cauchy_Orig:
			matrix = cauchy_original_coding_matrix(k, m, w);
			bitmatrix = new JER_Bitmatrix(*matrix);
			break;
		case Cauchy_Good:
			matrix = cauchy_good_general_coding_matrix(k, m, w);
			bitmatrix = new JER_Bitmatrix(*matrix);
			break;
		case Liberation:
			bitmatrix = liberation_coding_bitmatrix(k, w);
			break;
		case Blaum_Roth:
			bitmatrix = blaum_roth_coding_bitmatrix(k, w);
			break;
		case Liber8tion:
			bitmatrix = liber8tion_coding_bitmatrix(k);
	}
	gettimeofday(&t4, &tz);
	tsec = 0.0;
	tsec += t4.tv_usec;
	tsec -= t3.tv_usec;
	tsec /= 1000000.0;
	tsec += t4.tv_sec;
	tsec -= t3.tv_sec;
	totalsec += tsec;
	
	/* Begin decoding process */
	total = 0;
	n = 1;	
	while (n <= readins) {
		numerased = 0;
		/* Open files, check for erasures, read in data/coding */	
		for (i = 1; i <= k; i++) {
			sprintf(fname, "%s/Coding/%s_k%0*d%s", curdir, cs1, md, i, cs2);
			fp = fopen(fname, "rb");
			if (fp == NULL) {
				erased[i-1] = 1;
				erasures.push_back(i-1);
				numerased++;
			}
			else {
				if (buffersize == origsize) {
					stat(fname, &status);
					blocksize = status.st_size;
					slices->ptrs[i-1] = (unsigned char *)malloc(sizeof(unsigned char)*blocksize);
					fread(slices->ptrs[i-1], sizeof(unsigned char), blocksize, fp);
				}
				else {
					fseek(fp, blocksize*(n-1), SEEK_SET); 
					fread(slices->ptrs[i-1], sizeof(unsigned char), buffersize/k, fp);
				}
				fclose(fp);
			}
		}
		for (i = 1; i <= m; i++) {
			sprintf(fname, "%s/Coding/%s_m%0*d%s", curdir, cs1, md, i, cs2);
			fp = fopen(fname, "rb");
			if (fp == NULL) {
				erased[k+(i-1)] = 1;
				erasures.push_back(k+i-1);
				numerased++;
				//printf("%s failed\n", fname);
			}
			else {
				if (buffersize == origsize) {
					stat(fname, &status);
					blocksize = status.st_size;
					slices->ptrs[k+i-1] = (unsigned char *)malloc(sizeof(unsigned char)*blocksize);
					fread(slices->ptrs[k+i-1], sizeof(unsigned char), blocksize, fp);
				}
				else {
					fseek(fp, blocksize*(n-1), SEEK_SET);
					fread(slices->ptrs[k+i-1], sizeof(unsigned char), blocksize, fp);
				}
				fclose(fp);
			}
		}
		//set the blocksize
		slices->size = blocksize;

		/* Finish allocating data/coding if needed */
		if (n == 1) {
			for (i = 0; i < numerased; i++) {
				if (erasures[i] < k) {
					slices->ptrs[erasures[i]] = (unsigned char *)malloc(sizeof(unsigned char)*blocksize);
				}
				else {
					slices->ptrs[erasures[i]] = (unsigned char *)malloc(sizeof(unsigned char)*blocksize);
				}
			}
		}

		gettimeofday(&t3, &tz);
	
		/* Choose proper decoding method */
		if (tech == Reed_Sol_Van || tech == Reed_Sol_R6_Op) {
			i = JER_Matrix_Decode(slices, matrix, 1, erasures);
		}
		else if (tech == Cauchy_Orig || tech == Cauchy_Good || tech == Liberation || tech == Blaum_Roth || tech == Liber8tion) {
			i = JER_Schedule_Decode_Lazy(slices, bitmatrix, erasures, 1);
		}
		else {
			fprintf(stderr, "Not a valid coding technique.\n");
			exit(0);
		}
		gettimeofday(&t4, &tz);
	
		/* Exit if decoding was unsuccessful */
		if (i == -1) {
			fprintf(stderr, "Unsuccessful!\n");
			exit(0);
		}
	
		/* Create decoded file */
		sprintf(fname, "%s/Coding/%s_decoded%s", curdir, cs1, cs2);
		if (n == 1) {
			fp = fopen(fname, "wb");
		}
		else {
			fp = fopen(fname, "ab");
		}
		for (i = 0; i < k; i++) {
			if (total+blocksize <= origsize) {
				fwrite(slices->ptrs[i], sizeof(char), blocksize, fp);
				total+= blocksize;
			}
			else {
				for (j = 0; j < blocksize; j++) {
					if (total < origsize) {
						fprintf(fp, "%c", slices->ptrs[i][j]);
						total++;
					}
					else {
						break;
					}
					
				}
			}
		}
		n++;
		fclose(fp);
		tsec = 0.0;
		tsec += t4.tv_usec;
		tsec -= t3.tv_usec;
		tsec /= 1000000.0;
		tsec += t4.tv_sec;
		tsec -= t3.tv_sec;
		totalsec += tsec;
	}
	
	/* Free allocated memory */
	for(i=0;i<k+m;i++){
		free(slices->ptrs[i]);
	}
	free(cs1);
	free(fname);
	
	/* Stop timing and print time */
	gettimeofday(&t2, &tz);
	tsec = 0;
	tsec += t2.tv_usec;
	tsec -= t1.tv_usec;
	tsec /= 1000000.0;
	tsec += t2.tv_sec;
	tsec -= t1.tv_sec;
	printf("Decoding (MB/sec): %0.10f\n", (origsize/1024/1024)/totalsec);
	printf("De_Total (MB/sec): %0.10f\n\n", (origsize/1024/1024)/tsec);
}	

void ctrl_bs_handler(int dummy) {
	time_t mytime;
	mytime = time(0);
	fprintf(stderr, "\n%s\n", ctime(&mytime));
	fprintf(stderr, "You just typed ctrl-\\ in decoder.c\n");
	fprintf(stderr, "Total number of read ins = %d\n", readins);
	fprintf(stderr, "Current read in: %d\n", n);
	fprintf(stderr, "Method: %s\n\n", Methods[method]);
	signal(SIGQUIT, ctrl_bs_handler);
}
